AddCSLuaFile("cl_init.lua")
AddCSLuaFile("shared.lua")

include("shared.lua")

function ENT:Think()
	local curhealth = self:GetVehicleHealth()
	if curhealth < self.MaxHealth and self:GetStunTime() <= CurTime() then
		self:SetVehicleHealth(curhealth + FrameTime() * 120)
	end

	local owner = self:GetVehicleOwner()
	if owner:IsValid() then
		owner:SetPos(self:GetPos())
		
		if GAMEMODE.Winner == owner then
			self:SetMaterial("models/shiny")
		else
			self:SetMaterial("")
		end

		if owner:GetInfoNum("hovergear_vcol_type", 1) == 3 then
			local r, g, b = owner:GetInfoNum("hovergear_vcol_r", 0), owner:GetInfoNum("hovergear_vcol_g", 0), owner:GetInfoNum("hovergear_vcol_b", 0)
			self:SetColor(Color(r, g, b, 255))
		elseif owner:GetInfoNum("hovergear_vcol_type", 1) == 2 then
			local color = Vector(owner:GetInfo("cl_weaponcolor"))*255
			local r, g, b = color.x, color.y, color.z
			self:SetColor(Color(r, g, b, 255))
		elseif owner:GetInfoNum("hovergear_vcol_type", 1) == 1 then
			local color = Vector(owner:GetInfo("cl_playercolor"))*255
			local r, g, b = color.x, color.y, color.z
			self:SetColor(Color(r, g, b, 255))
		elseif owner:GetInfoNum("hovergear_vcol_type", 1) == 0 then
			self:SetColor(Color(255, 255, 255, 255))
		end
	end
end

function ENT:PhysicsCollide(data, phys)
	if 300 * SCALE <= data.Speed and data.HitNormal:Distance(self:GetUp() * -1) > 1 then
		local ent = data.HitEntity
		if not (ent:IsValid() and ent:GetPhysicsObject():IsValid() and ent:GetPhysicsObject():GetMass() < 100) then
			--self:SetVehicleHealth(self:GetVehicleHealth() - data.Speed * 0.025 * RSCALE)
			self:TakeDamage(data.Speed * 0.025 * RSCALE)
		end

		if self.NextCollisionSound <= CurTime() then
			self.NextCollisionSound = CurTime() + 0.025

			if 1800 * SCALE <= data.Speed then
				self:EmitSound("vehicles/v8/vehicle_impact_heavy"..math.random(1, 4)..".wav")
			else
				self:EmitSound("vehicles/v8/vehicle_impact_medium"..math.random(1, 4)..".wav")
			end

			local effectdata = EffectData()
				effectdata:SetOrigin(data.HitPos)
			util.Effect("carhit", effectdata)
		end
	end
end

function ENT:OnTakeDamage(dmginfo)
	local damage = dmginfo:GetDamage()
	if damage > 0 then
		self:SetVehicleHealth(self:GetVehicleHealth() - damage)
		self:SetStunTime(CurTime() + 3)
	end

	self:TakePhysicsDamage(dmginfo)
end

DEHOVERPOWER = -15
HOVERLENGTH = 750 * SCALE
MAXHOVERLENGTH = 250 * SCALE
FORWARDPOWER = 650 * SCALE
BACKWARDPOWER = 500 * SCALE
STRAFEPOWER = 500 * SCALE
TURNPOWER = 350
ROLLPOWER = 400
POWER1 = 50
POWER2 = 200
MAXPOWER2 = 0.75
ACTIVEDAMPING = 2.5
INACTIVEDAMPING = 1
STICKPOWER = 20
MAXSTICKPOWER = 100
HOVERDISTANCE = 0.3
DRIFTMUL = 1.6
ABSMUL = 0.25
function ENT:PhysicsSimulate(phys, frametime)
	phys:Wake()

	local owner = self:GetVehicleOwner()

	if not owner:IsValid() or self:GetVehicleHealth() == 0 then
		phys:SetDamping(0.1 * SCALE, 0.1)
		phys:SetMaterial("metal")
		--phys:EnableGravity(true)
		return SIM_NOTHING
	end

	--phys:EnableGravity(false)

	local mass = phys:GetMass()
	local vPos = self:GetPos()
	local aAng = self:GetAngles()
	local fwd = aAng:Forward()
	local up = aAng:Up()
	local right = aAng:Right()
	local tracevec = up * -HOVERLENGTH
	local vAddVec = Vector(0, 0, 0)
	local tHoverTrace = self.HoverTrace

	local frontleft = vPos + fwd * self.Front + right * self.Left
	local frontright = vPos + fwd * self.Front + right * self.Right
	local backleft = vPos + fwd * self.Back + right * self.Left
	local backright = vPos + fwd * self.Back + right * self.Right

	local hits = 0
	local dist = 0

	tHoverTrace.start = frontleft
	tHoverTrace.endpos = frontleft + tracevec
	local tr = util.TraceLine(tHoverTrace)
	if tr.Hit and 0 < tr.Fraction and tr.HitNormal:Distance(up) <= 1 and not tr.HitSky then
		hits = hits + 1

		phys:ApplyForceOffset(frametime * mass * DEHOVERPOWER * tr.HitNormal, tHoverTrace.endpos)
		dist = dist + math.min(MAXHOVERLENGTH, tr.HitPos:Distance(tHoverTrace.start))
	end

	tHoverTrace.start = frontright
	tHoverTrace.endpos = frontright + tracevec
	local tr = util.TraceLine(tHoverTrace)
	if tr.Hit and 0 < tr.Fraction and tr.HitNormal:Distance(up) <= 1 and not tr.HitSky then
		hits = hits + 1

		phys:ApplyForceOffset(frametime * mass * DEHOVERPOWER * tr.HitNormal, tHoverTrace.endpos)
		dist = dist + math.min(MAXHOVERLENGTH, tr.HitPos:Distance(tHoverTrace.start))
	end

	tHoverTrace.start = backleft
	tHoverTrace.endpos = backleft + tracevec
	local tr = util.TraceLine(tHoverTrace)
	if tr.Hit and 0 < tr.Fraction and tr.HitNormal:Distance(up) <= 1 and not tr.HitSky then
		hits = hits + 1

		phys:ApplyForceOffset(frametime * mass * DEHOVERPOWER * tr.HitNormal, tHoverTrace.endpos)
		dist = dist + math.min(MAXHOVERLENGTH, tr.HitPos:Distance(tHoverTrace.start))
	end

	tHoverTrace.start = backright
	tHoverTrace.endpos = backright + tracevec
	local tr = util.TraceLine(tHoverTrace)
	if tr.Hit and 0 < tr.Fraction and tr.HitNormal:Distance(up) <= 1 and not tr.HitSky then
		hits = hits + 1

		phys:ApplyForceOffset(frametime * mass * DEHOVERPOWER * tr.HitNormal, tHoverTrace.endpos)
		dist = dist + math.min(MAXHOVERLENGTH, tr.HitPos:Distance(tHoverTrace.start))
	end

	dist = dist / hits

	if 0 < hits then
		phys:SetVelocityInstantaneous(phys:GetVelocity() + frametime * STICKPOWER * (MAXHOVERLENGTH * HOVERDISTANCE - dist) * up + Vector(0, 0, frametime * SCALE * 800))

		phys:SetMaterial("gmod_ice")
		phys:SetDamping(0.1 * SCALE, ACTIVEDAMPING)
		if owner:KeyDown(IN_FORWARD) then
			--vAddVec = vAddVec + FORWARDPOWER * (self:GetVehicleHealth() / self.MaxHealth) * math.max(1, 2 * (1 - math.abs(fwd:Dot(phys:GetVelocity():Normalize())))) * fwd
			vAddVec = vAddVec + FORWARDPOWER * (self:GetVehicleHealth() / self.MaxHealth) * math.max(1, 2 * fwd:Distance(phys:GetVelocity():GetNormal()) * DRIFTMUL) * fwd
		elseif owner:KeyDown(IN_BACK) then
			local back = fwd * -1
			--vAddVec = vAddVec + BACKWARDPOWER * (self:GetVehicleHealth() / self.MaxHealth) * math.max(1, back:Distance(phys:GetVelocity():Normalize()) * DRIFTMUL) * back
			vAddVec = vAddVec + BACKWARDPOWER * (self:GetVehicleHealth() / self.MaxHealth) * math.max(1, back:Distance(phys:GetVelocity():GetNormal())) * back
		end
	else
		phys:SetMaterial("metal")
		phys:SetDamping(0.1 * SCALE, INACTIVEDAMPING)
		if owner:KeyDown(IN_FORWARD) then
			phys:AddAngleVelocity(Vector(0, frametime * -ROLLPOWER, 0))
		elseif owner:KeyDown(IN_BACK) then
			phys:AddAngleVelocity(Vector(0, frametime * ROLLPOWER, 0))
		end
	end

	if owner:KeyDown(IN_JUMP) then
		if owner:KeyDown(IN_MOVELEFT) then
			if 0 < hits then
				local left = right * -1
				vAddVec = vAddVec + STRAFEPOWER * (self:GetVehicleHealth() / self.MaxHealth) * math.max(1, left:Distance(phys:GetVelocity():GetNormal()) * DRIFTMUL) * left
			else
				phys:AddAngleVelocity(Vector(frametime * -ROLLPOWER, 0, 0))
			end
		elseif owner:KeyDown(IN_MOVERIGHT) then
			if 0 < hits then
				vAddVec = vAddVec + STRAFEPOWER * (self:GetVehicleHealth() / self.MaxHealth) * math.max(1, right:Distance(phys:GetVelocity():GetNormal()) * DRIFTMUL) * right
			else
				phys:AddAngleVelocity(Vector(frametime * ROLLPOWER, 0, 0))
			end
		end
	else
		if owner:KeyDown(IN_MOVELEFT) then
			phys:AddAngleVelocity(Vector(0, 0, frametime * TURNPOWER))
			if 0 < hits then
				phys:AddAngleVelocity(Vector(frametime * -ROLLPOWER * 0.5, 0, 0))
			end
		elseif owner:KeyDown(IN_MOVERIGHT) then
			phys:AddAngleVelocity(Vector(0, 0, frametime * -TURNPOWER))
			if 0 < hits then
				phys:AddAngleVelocity(Vector(frametime * ROLLPOWER * 0.5, 0, 0))
			end
		end
	end

	phys:ApplyForceCenter(frametime * mass * vAddVec)

	return SIM_NOTHING
end

function ENT:RemoveAllStatus(bSilent, bInstant)
	if bInstant then
		for _, ent in pairs(ents.FindByClass("status_*")) do
			if not ent.NoRemoveOnDeath and ent:GetOwner() == self then
				ent:Remove()
			end
		end
	else
		for _, ent in pairs(ents.FindByClass("status_*")) do
			if not ent.NoRemoveOnDeath and ent:GetOwner() == self then
				ent.SilentRemove = bSilent
				ent:SetDie()
			end
		end
	end
end

function ENT:RemoveStatus(sType, bSilent, bInstant, sExclude)
	local removed
	for _, ent in pairs(ents.FindByClass("status_"..sType)) do
		if sExclude and ent:GetClass() ~= sExclude then
			if ent:GetOwner() == self then
				if bInstant then
					ent:Remove()
				else
					ent.SilentRemove = bSilent
					ent:SetDie()
				end
				removed = true
			end
		end
	end
	return removed
end

function ENT:GetStatus(sType)
	local ent = self["status_"..sType]
	if ent and ent:IsValid() and ent:GetOwner() == self then return ent end
end

function ENT:GiveStatus(sType, fDie)
	local cur = self:GetStatus(sType)
	if cur then
		if fDie then
			cur:SetDie(fDie)
		end
		cur:SetVehicle(self, true)
		return cur
	else
		local ent = ents.Create("status_"..sType)
		if ent:IsValid() then
			ent:Spawn()
			if fDie then
				ent:SetDie(fDie)
			end
			ent:SetVehicle(self)
			return ent
		end
	end
end

local MASK_HOVER = bit.bor(MASK_SOLID, CONTENTS_OPAQUE, CONTENTS_GRATE, CONTENTS_SOLID, CONTENTS_WATER, CONTENTS_SLIME, CONTENTS_WINDOW)
function ENT:Initialize()
	self:SetModel("models/props_vehicles/car002a_physics.mdl")
	self:SetVehicleHealth(self.MaxHealth)
	self:SetUseType(SIMPLE_USE)

	self:PhysicsInitBox(Vector(-75.5, -37, -24) * SCALE, Vector(75.5, 37, 32) * SCALE)
	self:SetCollisionBounds(Vector(-75.5, -37, -24) * SCALE, Vector(75.5, 37, 32) * SCALE)
	self:SetSolid(SOLID_VPHYSICS)

	local phys = self:GetPhysicsObject()
	if phys:IsValid() then
		phys:SetMaterial("metal")
		phys:SetMass(750)
		phys:SetBuoyancyRatio(14)
		phys:SetDamping(0.1 * SCALE, ACTIVEDAMPING)
		phys:EnableMotion(true)
		phys:EnableDrag(false)
		phys:Wake()
	end

	self.NextCollisionSound = 0

	self:StartMotionController()

	self.HoverTrace = {filter = self, mask = MASK_HOVER}

	self.Maxs = self:OBBMaxs()
	self.Mins = self:OBBMins()
	self.Bottom = self.Mins.z
	self.Top = self.Maxs.z
	self.Front = self.Maxs.x
	self.Back = self.Mins.x
	self.Left = self.Mins.y
	self.Right = self.Maxs.y
	
	self.Powerups = {}
end